<?php
// +------------------------------------------------------------------
// | Cls-Token [ USER LOGIN AUTHENTICATION TOKEN ]
// | Copyright (c) 2024 All rights reserved.
// | Based on ThinkPHP 6 | 8
// | Licensed Mulan PSL v2 ( http://license.coscl.org.cn/MulanPSL2 )
// | Author: CLS <422064377>
// | CreateDate: 2024-07-12
// +------------------------------------------------------------------

namespace chleniang\ClsToken\driver;

use chleniang\ClsToken\Driver;
use chleniang\ClsToken\exception\ClsTokenException;
use think\helper\Arr;

class Redis extends Driver
{

    private static string $all_prefix = "CLS_TK:";

    /**
     * 当前驱动配置参数 (基础配置参数在属性 $base_options)
     * @var array
     */
    protected array $options = [
        "host"       => "127.0.0.1",
        "port"       => 6379,
        "password"   => "",
        "select"     => 1,
        "timeout"    => 0,
        "prefix"     => "_APP_:",
        "persistent" => false,
    ];

    public function __construct(array $options = [])
    {
        parent::__construct($options);
        $this->setPrefix($this->options['prefix']);
        $this->handler = $this->getRedisClient();
    }

    /**
     * @throws ClsTokenException
     */
    public function set(array $tokenData)
    {
        if (
            !empty($tokenData['refresh_token'])
            && (1 > $tokenData['refresh_expire'])
        ) {
            throw new ClsTokenException([
                'msg' => '[R]Token expire time invalid.',
            ]);
        }

        $ttlToken   = (int)$tokenData['expire'] - (int)$tokenData['update_time'];
        $ttlRefresh = (int)$tokenData['refresh_expire'] - (int)$tokenData['update_time'];

        $this->handler->set(
            self::getStoreKey((string)$tokenData['user_identifier']),
            $tokenData,
            max($ttlToken, $ttlRefresh, 1)
        );
    }

    /**
     * 获取指定用户的token记录数据
     *      没找到返回空数组
     *
     * @param int|string $userIdentifier 用户标识(id/uuid)
     *
     * @return array
     */
    public function get(int|string $userIdentifier)
    {
        $info = $this->getOriginData($userIdentifier);

        $info['expire'] = $this->base_options['expire'];
        if (!empty($info['refresh_token'])) {
            $info['refresh_expire'] = $this->base_options['refresh_expire'];
        }

        return $info;
    }

    public function delete(int|string $userIdentifier)
    {
        return $this->handler->delete(
            self::getStoreKey((string)$userIdentifier)
        );
    }


    /**
     * 指定存储KEY的前缀
     *       (本redis驱动特有方法)
     *
     * @param string $prefix 前缀字符串
     *
     * @return $this
     */
    public function setPrefix(string $prefix)
    {
        $setStr                  = empty($prefix) ? "_AoM_:" : $prefix;
        $this->options['prefix'] = self::$all_prefix . $setStr;

        $this->handler = $this->getRedisClient();

        return $this;
    }

    private function getRedisClient()
    {
        $redisOption = $this->options;

        $redisOption['expire'] = $this->base_options['refresh_token'] ?
            max((int)$this->base_options['expire'], (int)$this->base_options['refresh_expire'], 1)
            : (int)$this->base_options['expire'];
        Arr::forget($redisOption, ['type']);
        return new \think\cache\driver\Redis($redisOption);
    }

    protected function getOriginData(int|string $userIdentifier)
    {
        return $this->handler->get(
            self::getStoreKey((string)$userIdentifier),
            []
        );
    }

    /**
     * 获取存储key(不含前缀,前缀已在redis选项中指定)
     *
     * @param string $userIdentifier 用户唯一标识(id/uuid)
     *
     * @return string
     */
    private static function getStoreKey(string $userIdentifier): string
    {
        return 'USR_' . $userIdentifier . '__tk';
    }

}